

#include <sys/socket.h>
#include <linux/if.h>
#include <linux/if_ether.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <linux/if_arp.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


static void  _dump(const char * buffer, int size)
{
    int         i, j, t;
    for(i=0; i<size; i+=16)
    {
        printf("%08x: ", i);
        t = 16;
        if(i + 16 > size)
            t = size - i;
        for(j=0; j<t; j++)
        {
            printf("%02x ", buffer[i+j] & 0xff);
        }
        for(j=t; j<16; j++)
        {
            printf("   ");
        }
        printf("\t");
        for(j=0; j<t; j++)
        {
            if(buffer[i+j] >= 36 && buffer[i+j] <= 126)
                printf("%c", buffer[i+j] & 0xff);
            else
                printf(".");
        }
        printf("\n");
    }
    return;
}

static const char * ETH = "eth0";

static int get_mac_and_ip(char * mac, char * ip)
{
    struct  ifreq       tmp;
    int                 sock;
    struct sockaddr_in  sin;
    int                 i;
    sock = socket(AF_INET, SOCK_STREAM, 0);
    if(sock == -1)
    {
        perror("socket");
        return -1;
    }

    if(mac != NULL)
    {
        memset(&tmp, 0, sizeof(tmp));
        strncpy(tmp.ifr_name, ETH, sizeof(tmp.ifr_name)-1);
        if( ioctl(sock, SIOCGIFHWADDR, &tmp) < 0 )
        {
            perror("ioctl.SIOCGIFHWADDR");
            return -1;
        }
        for(i=0; i<6; i++)
        {
            printf("%02x", tmp.ifr_hwaddr.sa_data[i] & 0xff);
            if(i == 5)
                printf("\n");
            else
                printf(":");
        }
        memcpy(mac, tmp.ifr_hwaddr.sa_data, 6);
    }
    if(ip != NULL)
    {
        memset(&tmp, 0, sizeof(tmp));
        strncpy(tmp.ifr_name, ETH, sizeof(tmp.ifr_name)-1);
        if( ioctl(sock, SIOCGIFADDR, &tmp) < 0 )
        {
            perror("ioctl.SIOCGIFADDR");
            return -1;
        }
        memcpy(&sin, &tmp.ifr_addr, sizeof(sin));
        printf("%s\n", inet_ntoa(sin.sin_addr));
        strcpy(ip, inet_ntoa(sin.sin_addr));
    }
    close(sock);
    return 0;
}

struct arpMsg 
{
    struct ethhdr ethhdr;       /* Ethernet header */
    u_short htype;              /* hardware type (must be ARPHRD_ETHER) */
    u_short ptype;              /* protocol type (must be ETH_P_IP) */
    u_char  hlen;               /* hardware address length (must be 6) */
    u_char  plen;               /* protocol address length (must be 4) */
    u_short operation;          /* ARP opcode */
    u_char  sHaddr[6];          /* sender's hardware address */
    u_char  sInaddr[4];         /* sender's IP address */
    u_char  tHaddr[6];          /* target's hardware address */
    u_char  tInaddr[4];         /* target's IP address */
    u_char  pad[18];            /* pad for min. Ethernet payload (60 bytes) */
};



static int  arpsock = -1;

int main()
{
    char                mac[6];
    char                ip[30];
    int                 optval = 1;
    in_addr_t           ipa;
    struct  arpMsg      msg;
    struct sockaddr     addr;

    get_mac_and_ip(mac, ip);
    ipa = inet_addr(ip);
    ipa = inet_addr("10.0.0.188");

    arpsock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ARP));
    if(arpsock == -1)
    {
        perror("socket");
        return -1;
    }
    if(setsockopt(arpsock, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval)) == -1)
    {
        perror("setsockopt");
        return -1;
    }


    struct ifreq    ifr;
    strcpy(ifr.ifr_name, "eth0");
    if(ioctl(arpsock, SIOCGIFINDEX, &ifr) != 0)
        perror("ioctl.SIOCGIFINDEX");
    printf("ifindex = %d\n", ifr.ifr_ifindex);
    struct sockaddr_ll  sll;
    memset(&sll, 0, sizeof(sll));
    sll.sll_family = AF_PACKET;
    sll.sll_ifindex = ifr.ifr_ifindex;
    sll.sll_protocol = htons(ETH_P_ALL);
    if(bind(arpsock, (struct sockaddr *)&sll, sizeof(sll)) != 0)
        perror("bind");

    memset(&msg, 0, sizeof(msg));
    memset(msg.ethhdr.h_dest, 0xff, 6);
    memcpy(msg.ethhdr.h_source, mac, 6);
    memcpy(msg.sHaddr, mac, 6);
    memcpy(msg.sInaddr, &ipa, 4);
    memcpy(msg.tInaddr, &ipa, 4);
    msg.ethhdr.h_proto  = htons(ETH_P_ARP);
    msg.htype           = htons(ARPHRD_ETHER);
    msg.ptype           = htons(ETH_P_IP);
    msg.hlen            = 6;
    msg.plen            = 4;
    msg.operation       = htons(ARPOP_REQUEST);

    _dump((const char *)&msg, sizeof(msg));
    memset(&addr, 0, sizeof(addr));
    addr.sa_family = PF_PACKET;
    strcpy(addr.sa_data, ETH);

    sendto(arpsock, &msg, sizeof(msg), 0, (struct sockaddr *)&sll, sizeof(sll));

    while(recv(arpsock, &msg, sizeof(msg), 0) > 0)
    {
        if (msg.ethhdr.h_proto != htons(ETH_P_ARP)) {
            // printf("not arp packet.\n");
            continue;
        }

        printf("____ receive arp packet:\n");
        _dump((const char *)&msg, sizeof(msg));

        char    ipsrc[4], ipdst[4], ethsrc[6], ethdst[6];
        memcpy(ipsrc, msg.sInaddr, 4);
        memcpy(ipdst, msg.tInaddr, 4);
        memcpy(ethsrc, msg.sHaddr, 6);
        memcpy(ethdst, msg.tHaddr, 6);

        memcpy(&ipa, msg.sInaddr, 4);
        struct in_addr  in;
        in.s_addr = ipa;

        in_addr_t t;
        memcpy(&t, msg.tInaddr, 4);
        struct in_addr q;
        q.s_addr = t;
        
        char    src[100], dst[100];
        strcpy(src, inet_ntoa(in));
        strcpy(dst, inet_ntoa(q));
        printf("[%s] query [%s]\n", src, dst);
        
        if (msg.operation == htons(ARPOP_REQUEST)) {
            memset(&msg, 0, sizeof(msg));
            memcpy(msg.ethhdr.h_dest, ethsrc, 6);
            memcpy(msg.ethhdr.h_source, mac, 6);
            memcpy(msg.sHaddr, mac, 6);
            memcpy(msg.sInaddr, ipdst, 4);
            memcpy(msg.tInaddr, ipsrc, 4);
            msg.ethhdr.h_proto  = htons(ETH_P_ARP);
            msg.htype           = htons(ARPHRD_ETHER);
            msg.ptype           = htons(ETH_P_IP);
            msg.hlen            = 6;
            msg.plen            = 4;
            msg.operation       = htons(ARPOP_REPLY);
            sendto(arpsock, &msg, sizeof(msg), 0, (struct sockaddr *)&sll, sizeof(sll));
        }
    }

    return 0;
}


